<?php

declare(strict_types=1);


namespace App\CustHub\Service;

use App\CustHub\Constants\UserMoneyLogTypeConstants;
use App\CustHub\Helpers\BcMath;
use App\CustHub\Mapper\ChUserMapper;
use App\CustHub\Mapper\ChUserMoneyLogMapper;
use App\CustHub\Model\ChUser;
use App\CustHub\Vo\UserBalanceVo;
use FriendsOfHyperf\Lock\Annotation\Lock;
use FriendsOfHyperf\Lock\Exception\LockTimeoutException;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Mine\Abstracts\AbstractService;
use Mine\Exception\NormalStatusException;
use function FriendsOfHyperf\Lock\lock;
use function Hyperf\Support\optional;

class UserBalanceService extends AbstractService
{
    #[Inject]
    protected ChUserMapper $userMapper;

    #[Inject]
    protected ChUserMoneyLogMapper $userMoneyLogMapper;

    /**
     * 变更用户余额
     * @param UserBalanceVo $vo
     * @return bool
     */
    public function updateBalance(UserBalanceVo $vo): bool
    {
        $lock = lock("user_balance_lock:".$vo->getUid(), 4);
        Db::beginTransaction();
        try {
            $lock->block(2);
            // 等待最多5秒后获取的锁...
            $userInfo =  ChUser::query()->where('id',$vo->getUid())->lockForUpdate()->first();;

            if(!empty($userInfo)){
                //变动前余额
                $beforeMoney = $userInfo->money;
                //变动后余额
                $after =  BcMath::add($beforeMoney,$vo->getMoney());
                if($after < 0){
                    throw new NormalStatusException('余额不足');
                }

                $updateData['money'] = $after;
                if($vo->getBusinessType() == UserMoneyLogTypeConstants::RECHARGE){
                    //累计充值金额
                    $totalRecharge = BcMath::add($userInfo['total_recharge'],$vo->getMoney());
                    $updateData['total_recharge'] = $totalRecharge;
                }
                //更新用户余额
                $rst = $this->userMapper->model::where('id',$vo->getUid())->update($updateData);
                if(!$rst){
                    throw new NormalStatusException('更新用户余额失败');
                }
                //记录变动日志
                if(empty($vo->getRemark())){
                    $remark = UserMoneyLogTypeConstants::getMessage($vo->getBusinessType());
                }else{
                    $remark = $vo->getRemark();
                }

                $logData = [
                    'uid' => $vo->getUid(),
                    'before' => $beforeMoney,
                    'money' => $vo->getMoney(),
                    'after' => $after,
                    'business_type' => $vo->getBusinessType(),
                    'remark' => $remark,
                    'order_id'=> $vo->getOrderId()
                ];

                $rst = $this->userMoneyLogMapper->save($logData);
                if(!$rst){
                    throw new NormalStatusException('更新用户余额-日志写入失败');
                }
            }
            Db::commit();
            $this->userMapper->clearCache($vo->getUid());
            return true;
        } catch (LockTimeoutException $e) {
            Db::rollBack();
            logger()->info('用户余额更新获取锁超时：'.$e->getMessage());
            return false;
        }catch (\Throwable|\RedisException $e){
            Db::rollBack();
            logger()->info('用户余额更新失败：'.$e->getMessage());
            return false;
        } finally {
            optional($lock)->release();
        }
    }
}